package com.qying.myguava.util;

import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

public interface MyConcurrentMap <K,V> extends Map<K,V> {

    @Override
    default V getOrDefault(Object key, V defaultValue){
        V v;
        return ((v = get(key)) != null) ? v : defaultValue;
    }

    @Override
    default void forEach(BiConsumer<? super K, ? super V> action){

        Objects.requireNonNull(action);

        for (Map.Entry<K,V> entry: entrySet()) {
            K k;V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            }catch (Exception e){
                continue;
            }
            action.accept(k,v);
        }
    }

    V putIfAbsent(K key, V value);

    boolean remove(Object key, Object value);

    boolean replace(K k, V oldValue, V newValue);

    V replace(K key, V value);

    default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function){
        Objects.requireNonNull(function);
        forEach ((k,v)->{
            while (!replace(k,v,function.apply(k,v))){
                if ((v=get(k)) == null) {
                    break;
                }
            }
        });
    }

    default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction){
        Objects.requireNonNull(mappingFunction);
        V v ,newValue;
        return ((v=get(key)) == null && (newValue = mappingFunction.apply(key)) != null
        && (v=putIfAbsent(key,newValue)) == null) ? newValue : v;
    }

    @Override
    default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        V oldValue;
        while ((oldValue = get(key)) != null) {
            V newValue = function.apply(key, oldValue);

            if (newValue != null) {
                if (replace(key, oldValue,newValue))
                    return newValue;
            }else if (remove(key, oldValue)) {
                return null;
            }
        }
        return oldValue;
    }

    @Override
    default V compute(K key, BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);

        V oldValue = get(key);
        for (;;) {
            V newValue = function.apply(key, oldValue);
            if (newValue == null) {
                if (oldValue != null || containsKey(key)) {
                    if (remove(key,oldValue)) {
                        return null;
                    }
                    oldValue = get(key);
                } else {
                    return null;
                }
            }else {
              if (oldValue != null) {
                  if (replace(key, oldValue, newValue)) {
                      return newValue;
                  }
                  oldValue = get(key);
              }else {
                  if ((oldValue = putIfAbsent(key, newValue)) == null) {
                      return newValue;
                  }
              }
            }
        }
    }

    default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> function) {

        Objects.requireNonNull(function);
        Objects.requireNonNull(value);

        V oldValue = get(key);

        for (;;) {
            if (oldValue != null) {
                V newValue = function.apply(oldValue,value);
                if (newValue != null) {
                    if (replace(key, oldValue, newValue)){
                        return newValue;
                    }
                }else if (remove(key, oldValue)) {
                    return null;
                }
            }else {
                if ((oldValue = putIfAbsent(key,value)) == null) {
                    return value;
                }
            }
        }
    }
}
